package com.jfirer.mvc.core;

import com.jfirer.baseutil.StringUtil;
import com.jfirer.baseutil.exception.UnSupportException;
import com.jfirer.baseutil.reflect.ReflectUtil;
import com.jfirer.baseutil.reflect.SimpleHotswapClassLoader;
import com.jfirer.mvc.config.MvcConfig;
import com.jfirer.mvc.core.action.Action;
import com.jfirer.mvc.core.action.ActionCenter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.List;

public class DispathServletHelper
{

    private static final Logger            logger = LoggerFactory.getLogger(DispathServletHelper.class);
    private volatile     ActionCenter      actionCenter;
    private final        ServletContext    servletContext;
    private final        RequestDispatcher staticResourceDispatcher;
    private final        PreHandler        preHandler;
    private final        MvcConfig         mvcConfig;
    private final        String            configClassName;

    public DispathServletHelper(String configClassName, final ServletContext servletContext)
    {
        this.servletContext = servletContext;
        this.configClassName = configClassName;
        staticResourceDispatcher = getStaticResourceDispatcher();
        actionCenter = ActionCenterBuilder.generate(Thread.currentThread().getContextClassLoader(), servletContext, configClassName);
        mvcConfig = actionCenter.mvcConfig();
        if (mvcConfig.isHotdev())
        {
            preHandler = new HotswapPreHandler(mvcConfig);
        }
        else
        {
            preHandler = new NopPreHandler();
        }
    }

    RequestDispatcher getStaticResourceDispatcher()
    {
        /**
         * Default Servlet name used by Tomcat, Jetty, JBoss, and GlassFish
         */
        final String COMMON_DEFAULT_SERVLET_NAME = "default";
        /** Default Servlet name used by Resin */
        final String RESIN_DEFAULT_SERVLET_NAME = "resin-file";
        /** Default Servlet name used by WebLogic */
        final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet";
        /** Default Servlet name used by WebSphere */
        final String      WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet";
        RequestDispatcher requestDispatcher              = null;
        if ((requestDispatcher = servletContext.getNamedDispatcher(COMMON_DEFAULT_SERVLET_NAME)) != null)
        {
        }
        else if ((requestDispatcher = servletContext.getNamedDispatcher(RESIN_DEFAULT_SERVLET_NAME)) != null)
        {
        }
        else if ((requestDispatcher = servletContext.getNamedDispatcher(WEBLOGIC_DEFAULT_SERVLET_NAME)) != null)
        {
        }
        else if ((requestDispatcher = servletContext.getNamedDispatcher(WEBSPHERE_DEFAULT_SERVLET_NAME)) != null)
        {
        }
        else
        {
            throw new UnSupportException("找不到默认用来处理静态资源的处理器");
        }
        return requestDispatcher;
    }

    public Action getAction(HttpServletRequest request)
    {
        return actionCenter.getAction(request);
    }

    public void handleStaticResourceRequest(HttpServletRequest request, HttpServletResponse response)
    {
        try
        {
            staticResourceDispatcher.forward(request, response);
        }
        catch (Exception e)
        {
            ReflectUtil.throwException(e);
        }
    }

    public MvcConfig getMvcConfig()
    {
        return mvcConfig;
    }

    public void preHandle(HttpServletRequest request, HttpServletResponse response)
    {
        try
        {
            request.setCharacterEncoding(mvcConfig.getEncode());
            response.setCharacterEncoding(mvcConfig.getEncode());
        }
        catch (UnsupportedEncodingException e)
        {
            ReflectUtil.throwException(e);
        }
        preHandler.preHandle();
    }

    interface PreHandler
    {
        void preHandle();
    }

    class NopPreHandler implements PreHandler
    {

        @Override
        public void preHandle()
        {
        }
    }

    class HotswapPreHandler implements PreHandler
    {

        class FileChangeDetect
        {
            private final File[] roots;
            private       long   lastModitySum = 0;

            public FileChangeDetect(File[] roots)
            {
                this.roots = roots;
                detectChange();
            }

            private long calculateModitySum(File file)
            {
                if (file.isDirectory())
                {
                    long sum = 0;
                    for (File each : file.listFiles())
                    {
                        sum += calculateModitySum(each);
                    }
                    return sum;
                }
                else
                {
                    return file.lastModified();
                }
            }

            public boolean detectChange()
            {
                long newModitySum = 0;
                for (File root : roots)
                {
                    newModitySum = calculateModitySum(root);
                }
                if (newModitySum == lastModitySum)
                {
                    return false;
                }
                else
                {
                    lastModitySum = newModitySum;
                    return true;
                }
            }
        }

        private final FileChangeDetect detect;
        private final String           reloadPath;
        private final String           reloadPackages;
        private final String           excludePackages;

        public HotswapPreHandler(MvcConfig mvcConfig)
        {
            logger.debug("使用热加载方式进行启动");
            List<File> roots = new LinkedList<File>();
            for (String each : mvcConfig.getMonitorPath().split(","))
            {
                roots.add(new File(each));
            }
            detect = new FileChangeDetect(roots.toArray(new File[roots.size()]));
            reloadPath = mvcConfig.getReloadPath();
            reloadPackages = mvcConfig.getReloadPackages();
            if (StringUtil.isNotBlank(mvcConfig.getExcludePackages()))
            {
                excludePackages = mvcConfig.getExcludePackages();
            }
            else
            {
                excludePackages = null;
            }
        }

        @Override
        public void preHandle()
        {
            if (detect.detectChange())
            {
                long                     t0          = System.currentTimeMillis();
                SimpleHotswapClassLoader classLoader = new SimpleHotswapClassLoader(reloadPath);
                classLoader.setReloadPackages(reloadPackages.split(","));
                if (excludePackages != null)
                {
                    classLoader.setExcludePackages(excludePackages.split(","));
                }
                actionCenter = ActionCenterBuilder.generate(classLoader, servletContext, configClassName);
                logger.debug("热部署,耗时:{}", System.currentTimeMillis() - t0);
            }
        }

    }


}
